package xyz.scootaloo.kami.server.service.impl

import io.vertx.core.Future
import xyz.scootaloo.kami.server.model.SysTask
import xyz.scootaloo.kami.server.model.dao.TaskDAO
import xyz.scootaloo.kami.server.standard.SyncMark
import xyz.scootaloo.kami.server.standard.currentTimeMillis
import xyz.scootaloo.kami.server.standard.getLogger
import xyz.scootaloo.kami.server.service.*
import xyz.scootaloo.kami.server.service.CrontabService.Crontab
import xyz.scootaloo.kami.server.verticle.vertx
import java.util.*

/**
 * @author flutterdash@qq.com
 * @since 2022/2/23 14:50
 */
object InternalCrontabServiceImpl : CrontabService, ApplicationStageListener {
    private val log = getLogger()

    private var running = false
    private val tasks = TreeMap<String, CrontabWrapper>()

    private val dbTaskHandlerMapper = mapOf(
        CrontabService.DatabaseCrontabHandler.UPLOAD_TASK to UploadService()
    )

    override fun afterDatabaseAvailable() {
        Sync.run {
            val dbTasks = TaskDAO.listIfRunning()
            addDbTasks(dbTasks)
            startCrontab()
            log.info("定时任务服务启动成功, 从数据库载入任务${dbTasks.size}个")
        }
    }

    override fun submit(task: Crontab): Future<Unit> {
        if (!running)
            return Future.failedFuture("定时任务服务未启动")
        return doSubmit(task)
    }

    private fun doSubmit(task: Crontab): Future<Unit> {
        val wrapper = CrontabWrapper.of(task)
        return Sync.run {
            log.info("创建定时任务`${task.name}`")
            addTask(wrapper)
        }
    }

    private fun addDbTasks(tks: List<SysTask>) {
        for (dbTask in tks) {
            if (dbTaskHandlerMapper.containsKey(dbTask.type)) {
                val handler = dbTaskHandlerMapper[dbTask.type]!!
                try {
                    handler.handle(dbTask)
                } catch (e: Throwable) {
                    log.error("解析数据库任务时遇到异常: ${e.message}", e)
                }
            } else {
                log.warn("没有找到数据库任务类型为`${dbTask.type}`的处理器")
            }
        }
    }

    @SyncMark
    private fun addTask(wrapper: CrontabWrapper) {
        tasks[wrapper.id] = wrapper
    }

    private fun startCrontab() {
        running = true
        vertx.setPeriodic(CrontabService.defDelay) {
            Sync.run { runTasks() }
        }
    }

    private fun runTasks() {
        val currentTime = currentTimeMillis()
        val invalidTasks = ArrayList<String>()
        for ((id, task) in tasks) {
            try {
                runTask(currentTime, task)
                if (!task.task.valid) {
                    invalidTasks.add(id)
                    continue
                }
            } catch (e: Throwable) {
                log.error("执行定时任务时遇到异常, 任务名${task.taskName}: ${e.message}", e)
            }
        }

        if (invalidTasks.isNotEmpty()) {
            for (invalidTaskId in invalidTasks) {
                val removed = tasks.remove(invalidTaskId)
                log.info("定时任务${removed?.taskName}注销")
            }
        }
    }

    private fun runTask(currentTime: Long, wrapper: CrontabWrapper) {
        val interval = currentTime - wrapper.lastExeTime
        if (interval > wrapper.task.delay) {
            wrapper.lastExeTime = currentTime
            wrapper.task.run()
        }
    }

    class CrontabWrapper(
        var lastExeTime: Long,
        val id: String,
        val task: Crontab
    ) {
        val taskName get() = task.name

        companion object {
            fun of(crontab: Crontab) = CrontabWrapper(
                lastExeTime = currentTimeMillis(),
                id = generateId(crontab),
                task = crontab
            )

            private fun generateId(crontab: Crontab): String {
                return "${crontab.order}${crontab.name}"
            }
        }
    }
}